import { Arc2 } from "../src/curve/Arc2";
import { Point2 } from "../src/point/Point2";
import { TestBase } from "./TestBase";
import { Precision } from "../src/Precision";
import { IntersectionType } from "../src/curve/IntersectionType";
import { Segment2 } from "../src/curve/Segment2";

export class TestArc2 extends TestBase {
    public async runAllTests() {
        console.log('开始运行 Arc2 测试...');

        this.testConstruction();
        this.testEndPoints();
        this.testLength();
        this.testContainPoint();
        this.testProjectPoint();
        this.testIntersection();
        this.testParameterization();
        this.testTangentAndCurvature();
        this.testEdgeCases();
        this.testClosedArc();

        console.log('Arc2 测试完成！');
    }

    /**
     * @description 测试圆弧构造
     */
    private testConstruction() {
        console.log('测试圆弧构造...');

        const center = new Point2(1, 1);
        const radius = 2;
        const startAngle = 0;
        const endAngle = Math.PI / 2;

        const arc = new Arc2(startAngle, endAngle, center, radius);

        // 测试基本属性
        this.assert(arc.center.equals(center), '圆心设置错误');
        this.assert(Math.abs(arc.radius - radius) < Precision.EPSILON5, '半径设置错误');
        this.assert(Math.abs(arc.startAngle - startAngle) < Precision.EPSILON5, '起始角度设置错误');
        this.assert(Math.abs(arc.endAngle - endAngle) < Precision.EPSILON5, '终止角度设置错误');

        // 测试端点位置
        const expectedStart = new Point2(3, 1);  // center + (radius, 0)
        const expectedEnd = new Point2(1, 3);    // center + (0, radius)
        this.assert(arc.start.equals(expectedStart), '起点位置错误');
        this.assert(arc.end.equals(expectedEnd), '终点位置错误');

        console.log('✓ 圆弧构造测试通过');
    }

    /**
     * @description 测试端点操作
     */
    private testEndPoints() {
        console.log('测试端点操作...');

        const arc = new Arc2(0, Math.PI, new Point2(0, 0), 1);

        // 测试起点和终点
        this.assert(arc.start.equals(new Point2(1, 0)), '起点计算错误');
        this.assert(arc.end.equals(new Point2(-1, 0)), '终点计算错误');

        // 测试克隆
        const cloned = arc.clone();
        this.assert(cloned.start.equals(arc.start) && cloned.end.equals(arc.end), '克隆错误');

        console.log('✓ 端点操作测试通过');
    }

    /**
     * @description 测试长度计算
     */
    private testLength() {
        console.log('测试长度计算...');

        const radius = 2;
        const center = new Point2(0, 0);

        // 测试四分之一圆
        const quarterArc = new Arc2(0, Math.PI / 2, center, radius);
        this.assert(Math.abs(quarterArc.length - radius * Math.PI / 2) < Precision.EPSILON5, '四分之一圆长度错误');

        // 测试半圆
        const halfArc = new Arc2(0, Math.PI, center, radius);
        this.assert(Math.abs(halfArc.length - radius * Math.PI) < Precision.EPSILON5, '半圆长度错误');

        // 测试完整圆
        const fullArc = new Arc2(0, 2 * Math.PI, center, radius);
        this.assert(Math.abs(fullArc.length - 2 * radius * Math.PI) < Precision.EPSILON5, '完整圆长度错误');

        console.log('✓ 长度计算测试通过');
    }

    /**
     * @description 测试点在圆弧上的判断
     */
    private testContainPoint() {
        console.log('测试点在圆弧上的判断...');

        const arc = new Arc2(0, Math.PI / 2, new Point2(0, 0), 1);

        // 测试端点
        this.assert(arc.containPoint(new Point2(1, 0)), '起点判断错误');
        this.assert(arc.containPoint(new Point2(0, 1)), '终点判断错误');

        // 测试圆弧上的点
        this.assert(arc.containPoint(new Point2(Math.cos(Math.PI / 4), Math.sin(Math.PI / 4))), '圆弧上点判断错误');

        // 测试圆弧外的点
        this.assert(!arc.containPoint(new Point2(2, 0)), '圆弧外点判断错误');
        this.assert(!arc.containPoint(new Point2(-1, 0)), '圆弧外点判断错误');
        this.assert(!arc.containPoint(new Point2(0, -1)), '圆弧外点判断错误');

        // 测试精度
        const nearPoint = new Point2(Math.cos(Math.PI / 4), Math.sin(Math.PI / 4) + Precision.EPSILON5);
        this.assert(arc.containPoint(nearPoint), '精度判断错误');

        console.log('✓ 点在圆弧上的判断测试通过');
    }

    /**
     * @description 测试点到圆弧的投影
     */
    private testProjectPoint() {
        console.log('测试点到圆弧的投影...');

        const arc = new Arc2(0, Math.PI / 2, new Point2(0, 0), 1);

        // 测试圆弧上点的投影
        const onArcPoint = new Point2(Math.cos(Math.PI / 4), Math.sin(Math.PI / 4));
        const proj1 = arc.projectPoint(onArcPoint);
        this.assert(proj1?.point.equals(onArcPoint), '圆弧上点投影错误');
        this.assert(Math.abs(proj1?.u - 0.5) < Precision.EPSILON5, '圆弧上点参数错误');

        // 测试圆内点的投影
        const innerPoint = new Point2(0.5, 0.5);
        const proj2 = arc.projectPoint(innerPoint);
        this.assert(proj2?.point.distance(arc.center) - arc.radius < Precision.EPSILON5, '圆内点投影错误');

        // 测试圆外点的投影
        const outerPoint = new Point2(2, 2);
        const proj3 = arc.projectPoint(outerPoint);
        this.assert(proj3?.point.distance(arc.center) - arc.radius < Precision.EPSILON5, '圆外点投影错误');

        console.log('✓ 点到圆弧的投影测试通过');
    }

    /**
     * @description 测试圆弧相交
     */
    private testIntersection() {
        console.log('测试圆弧相交...');

        const arc1 = new Arc2(0, Math.PI / 2, new Point2(0, 0), 1);

        // 测试与线段相交
        const segment = new Segment2(new Point2(-1, 0.5), new Point2(1, 0.5));
        const intersect1 = arc1.intersect(segment);
        this.assert(intersect1.type === IntersectionType.intersect, '与线段相交判断错误');
        this.assert(intersect1.intersectPoints.length === 1, '与线段交点数量错误');

        // 测试线段是圆弧的子集
        const segmentOnArc = new Segment2(
            new Point2(Math.cos(Math.PI / 6), Math.sin(Math.PI / 6)), // 30度
            new Point2(Math.cos(Math.PI / 3), Math.sin(Math.PI / 3))  // 60度
        );
        const intersect2 = arc1.intersect(segmentOnArc);
        this.assert(intersect2.type === IntersectionType.subSet, '线段子集判断错误');
        this.assert(intersect2.intersectPoints.length === 2, '线段子集端点数量错误');

        // 测试圆弧相交 - 超集关系
        const smallArc = new Arc2(Math.PI / 6, Math.PI / 3, new Point2(0, 0), 1); // 30度到60度的圆弧
        const intersect3 = arc1.intersect(smallArc);
        this.assert(intersect3.type === IntersectionType.superSet, '圆弧超集判断错误');
        this.assert(intersect3.intersectPoints.length === 0, '圆弧超集交点数量错误');

        // 测试圆弧相交 - 子集关系
        const largeArc = new Arc2(-Math.PI / 4, Math.PI, new Point2(0, 0), 1); // -45度到180度的圆弧
        const intersect4 = arc1.intersect(largeArc);
        this.assert(intersect4.type === IntersectionType.subSet, '圆弧子集判断错误');
        this.assert(intersect4.intersectPoints.length === 0, '圆弧子集交点数量错误');

        // 测试圆弧相交 - 相等
        const equalArc = new Arc2(0, Math.PI / 2, new Point2(0, 0), 1);
        const intersect5 = arc1.intersect(equalArc);
        this.assert(intersect5.type === IntersectionType.equal, '圆弧相等判断错误');
        this.assert(intersect5.intersectPoints.length === 2, '圆弧相等端点数量错误');

        // 测试圆弧相交 - 相交
        const intersectArc = new Arc2(Math.PI / 4, Math.PI * 3 / 4, new Point2(0.5, 0), 1);
        const intersect6 = arc1.intersect(intersectArc);
        this.assert(intersect6.type === IntersectionType.intersect, '圆弧相交判断错误');
        this.assert(intersect6.intersectPoints.length === 2, '圆弧相交点数量错误');

        // 测试圆弧相交 - 不相交
        const disjointArc = new Arc2(Math.PI, Math.PI * 3 / 2, new Point2(3, 0), 1);
        const intersect7 = arc1.intersect(disjointArc);
        this.assert(intersect7.type === IntersectionType.disjoint, '圆弧不相交判断错误');
        this.assert(intersect7.intersectPoints.length === 0, '圆弧不相交点数量错误');

        // 测试圆弧相交 - 重叠
        const overlapArc = new Arc2(Math.PI / 4, Math.PI * 2 / 3, new Point2(0, 0), 1);
        const intersect8 = arc1.intersect(overlapArc);
        this.assert(intersect8.type === IntersectionType.overlap, '圆弧重叠判断错误');
        this.assert(intersect8.intersectPoints.length === 2, '圆弧重叠端点数量错误');

        // 测试圆弧相交 - 相切
        const tangentArc = new Arc2(0, Math.PI / 2, new Point2(2, 0), 1);  // 在 (1,0) 点相切
        const intersect9 = arc1.intersect(tangentArc);
        this.assert(intersect9.type === IntersectionType.intersect, '圆弧相切判断错误');
        this.assert(intersect9.intersectPoints.length === 1, '圆弧相切点数量错误');
        this.assert(intersect9.intersectPoints[0].equals(new Point2(1, 0)), '圆弧相切点位置错误');

        // 测试线段相切
        const tangentSegment = new Segment2(new Point2(1, 0), new Point2(1, 1));  // 在 (1,0) 点相切
        const intersect10 = arc1.intersect(tangentSegment);
        this.assert(intersect10.type === IntersectionType.intersect, '线段相切判断错误');
        this.assert(intersect10.intersectPoints.length === 1, '线段相切点数量错误');
        this.assert(intersect10.intersectPoints[0].equals(new Point2(1, 0)), '线段相切点位置错误');

        console.log('✓ 圆弧相交测试通过');
    }

    /**
     * @description 测试切向量和曲率
     */
    private testTangentAndCurvature() {
        console.log('测试切向量和曲率...');

        const arc = new Arc2(0, Math.PI / 2, new Point2(0, 0), 2);

        // 测试起点切向量
        const startTangent = arc.tangentAt(0);
        this.assert(Math.abs(startTangent.x) < Precision.EPSILON5 &&
            Math.abs(startTangent.y - 1) < Precision.EPSILON5, '起点切向量错误');

        // 测试终点切向量
        const endTangent = arc.tangentAt(1);
        this.assert(Math.abs(endTangent.x + 1) < Precision.EPSILON5 &&
            Math.abs(endTangent.y) < Precision.EPSILON5, '终点切向量错误');

        // 测试曲率（应该是1/半径）
        const curvature = arc.curvatureAt(0.5);
        this.assert(Math.abs(curvature - 1 / 2) < Precision.EPSILON5, '曲率计算错误');

        console.log('✓ 切向量和曲率测试通过');
    }

    /**
     * @description 测试闭合圆弧
     */
    private testClosedArc() {
        console.log('测试闭合圆弧...');

        const arc = new Arc2(0, 2 * Math.PI, new Point2(0, 0), 1);

        // 测试是否闭合
        this.assert(arc.isClosed(), '闭合判断错误');

        // 测试起点终点重合
        this.assert(arc.start.equals(arc.end), '起点终点不重合');

        // 测试长度
        this.assert(Math.abs(arc.length - 2 * Math.PI) < Precision.EPSILON5, '闭合圆弧长度错误');

        console.log('✓ 闭合圆弧测试通过');
    }

    /**
     * @description 测试边界情况
     */
    private testEdgeCases() {
        console.log('测试边界情况...');

        // 测试零半径圆弧
        try {
            new Arc2(0, Math.PI, new Point2(0, 0), 0);
            this.assert(false, '应该拒绝零半径');
        } catch (e) {
            // 期望抛出异常
        }

        // 测试角度范围超过2π
        const largeArc = new Arc2(0, 3 * Math.PI, new Point2(0, 0), 1);
        this.assert(Math.abs(largeArc.length - 3 * Math.PI) < Precision.EPSILON5, '大角度范围处理错误');

        // 测试负角度
        const negativeArc = new Arc2(-Math.PI, 0, new Point2(0, 0), 1);
        this.assert(Math.abs(negativeArc.length - Math.PI) < Precision.EPSILON5, '负角度处理错误');

        console.log('✓ 边界情况测试通过');
    }

    /**
     * @description 测试圆弧参数化
     */
    private testParameterization() {
        console.log('测试圆弧参数化...');

        const center = new Point2(0, 0);
        const radius = 1;

        // 测试完整圆
        const fullCircle = new Arc2(0, 2 * Math.PI, center, radius);
        const fullCircleTests = [
            { param: 0, expected: new Point2(1, 0) },          // 0度
            { param: 0.25, expected: new Point2(0, 1) },       // 90度
            { param: 0.5, expected: new Point2(-1, 0) },       // 180度
            { param: 0.75, expected: new Point2(0, -1) }       // 270度
        ];

        // 测试参数点
        for (const test of fullCircleTests) {
            const point = fullCircle.d0(test.param);
            this.assert(point.equals(test.expected),
                `完整圆参数化错误: 参数 ${test.param} 应得到点 ${test.expected.toString()}, 实际得到 ${point.toString()}`);
        }

        // 测试部分圆弧
        const quarterCircle = new Arc2(0, Math.PI / 2, center, radius);
        const quarterTests = [
            { param: 0, expected: new Point2(1, 0) },                                      // 起点
            { param: 0.5, expected: new Point2(1 / Math.sqrt(2), 1 / Math.sqrt(2)) },         // 45度
            { param: 1, expected: new Point2(0, 1) }                                       // 终点
        ];

        for (const test of quarterTests) {
            const point = quarterCircle.d0(test.param);
            this.assert(point.equals(test.expected),
                `部分圆弧参数化错误: 参数 ${test.param} 应得到点 ${test.expected.toString()}, 实际得到 ${point.toString()}`);
        }

        // 测试跨越0度线的圆弧
        const crossZeroArc = new Arc2(-Math.PI / 4, Math.PI / 4, center, radius);
        const crossZeroTests = [
            { param: 0, expected: new Point2(Math.cos(-Math.PI / 4), Math.sin(-Math.PI / 4)) },   // 起点
            { param: 0.5, expected: new Point2(1, 0) },                                        // 中点
            { param: 1, expected: new Point2(Math.cos(Math.PI / 4), Math.sin(Math.PI / 4)) }      // 终点
        ];

        for (const test of crossZeroTests) {
            const point = crossZeroArc.d0(test.param);
            this.assert(point.equals(test.expected),
                `跨越0度线圆弧参数化错误: 参数 ${test.param} 应得到点 ${test.expected.toString()}, 实际得到 ${point.toString()}`);
        }

        // 测试参数范围验证
        try {
            quarterCircle.d0(-0.1);
            this.assert(false, '应该拒绝小于0的参数');
        } catch (e) {
            // 期望抛出异常
        }

        try {
            quarterCircle.d0(1.1);
            this.assert(false, '应该拒绝大于1的参数');
        } catch (e) {
            // 期望抛出异常
        }

        // 测试参数和点的互相转换
        const testPoints = [
            { param: 0.2, arc: quarterCircle },
            { param: 0.6, arc: quarterCircle },
            { param: 0.8, arc: quarterCircle }
        ];

        for (const test of testPoints) {
            const point = test.arc.d0(test.param);
            const recoveredParam = test.arc.getParameter(point);
            this.assert(Math.abs(recoveredParam - test.param) < Precision.EPSILON5,
                `参数和点的互相转换错误: 原参数 ${test.param}, 恢复参数 ${recoveredParam}`);
        }

        console.log('✓ 圆弧参数化测试通过');
    }
}
